/***************************************************************************************

***************************************************************************************/


/*
*************************************************************************************
* File name: bitstream.c
* Function: decode bitstream
*
*************************************************************************************
*/

#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "global.h"
#include "annexb.h"
#include "memalloc.h"
#include "biaridecod.h"

#define SVA_STREAM_BUF_SIZE 1024

extern StatBits *StatBitsPtr;	//ITM_r2
FILE *bitsfile = NULL;		//!< the bit stream file
unsigned char bit[8] = {0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};

typedef struct {
  FILE *f;
  unsigned char	buf[SVA_STREAM_BUF_SIZE]; 
  unsigned int	uClearBits;
  unsigned int	uPre3Bytes;
  int	iBytePosition;	
  int	iBufBytesNum;		
  int	iClearBitsNum;		
  int	iStuffBitsNum;		
  int iBitsCount;			
} InputStream;

InputStream IRABS;
InputStream *pIRABS = &IRABS;

void OpenIRABS(InputStream *p, char *fname)
{
  p->f = fopen(fname,"rb");
  if(p->f==NULL){printf ("\nCan't open file %s",fname);exit(-1);}

  p->uClearBits			= 0xffffffff;
  p->iBytePosition		= 0;
  p->iBufBytesNum			= 0;
  p->iClearBitsNum		= 0;
  p->iStuffBitsNum		= 0;
  p->iBitsCount			= 0;
  p->uPre3Bytes			= 0;
}

void CloseIRABS(InputStream *p)
{
  fclose(p->f);
}

/*
*************************************************************************
* Function: Check start code's type           
* Input:
* Output:
* Return: 
* Attention: If the start code is video_sequence_start_code,user_data_start_code
or extension_start_code, the demulation mechanism is forbidded.
* Author: 
*************************************************************************
*/
void CheckType(int startcode){
  startcode = startcode&0x000000ff;
  switch(startcode)
  {
  case 0xb0:
  case 0xb2:
  case 0xb5:
    demulate_enable = 0;
    break;  
  default:
    demulate_enable = 1;
    break;
  }
}
// move iBytePosition to the next byte of start code prefix
//return
//    0 : OK
//   -1 : arrive at stream end and start code is not found
//   -2 : p->iBytePosition error
//-------------------------------------------------------------------------
int NextStartCode(InputStream *p)
{
  int i, m;
  unsigned char a=0,b=0;	// a b 0 1 2 3 4 ... M-3 M-2 M-1
  m=0; 

  while(1)
  {
    if(p->iBytePosition >= p->iBufBytesNum - 2)	//if all bytes in buffer has been searched
    {
      m = p->iBufBytesNum - p->iBytePosition;
      if(m <0)  return -2;	// p->iBytePosition error
      if(m==1)  b=p->buf[p->iBytePosition+1];
      if(m==2){ b=p->buf[p->iBytePosition+1]; a=p->buf[p->iBytePosition];}
      p->iBufBytesNum = fread(p->buf,1,SVA_STREAM_BUF_SIZE,p->f);
      p->iBytePosition = 0;
    }

    if(p->iBufBytesNum + m < 3) 
      return -1;  //arrive at stream end and start code is not found

    if(m==1 && b==0 && p->buf[0]==0 && p->buf[1]==1)
    {
      p->iBytePosition	= 2;
      p->iClearBitsNum	= 0;
      p->iStuffBitsNum	= 0;
      p->iBitsCount		+= 24;
      p->uPre3Bytes		= 1;
      return 0;
    }

    if(m==2 && b==0 && a==0 && p->buf[0]==1)
    {
      p->iBytePosition	= 1;
      p->iClearBitsNum	= 0;
      p->iStuffBitsNum	= 0;
      p->iBitsCount		+= 24;
      p->uPre3Bytes		= 1;
      return 0;
    }

    if(m==2 && b==0 && p->buf[0]==0 && p->buf[1]==1)
    {
      p->iBytePosition	= 2;
      p->iClearBitsNum	= 0;
      p->iStuffBitsNum	= 0;
      p->iBitsCount		+= 24;
      p->uPre3Bytes		= 1;
      return 0;
    }

    for(i = p->iBytePosition; i < p->iBufBytesNum - 2; i++)
    {
      if(p->buf[i]==0 && p->buf[i+1]==0 && p->buf[i+2]==1)
      {
        p->iBytePosition	= i+3;
        p->iClearBitsNum	= 0;
        p->iStuffBitsNum	= 0;
        p->iBitsCount		+= 24;
        p->uPre3Bytes		= 1;
        return 0;
      }
      p->iBitsCount += 8;
    }
    p->iBytePosition = i;
  }
}

/*
*************************************************************************
* Function:
* Input:
* Output:
* Return:  0 : OK
-1 : arrive at stream end
-2 : meet another start code
* Attention:
*************************************************************************
*/
int ClearNextByte(InputStream *p)
{
  int i,k,j;
  unsigned char temp[3];
  i = p->iBytePosition;
  k = p->iBufBytesNum - i;
  if(k < 3)
  {
    for(j=0;j<k;j++) temp[j] = p->buf[i+j];
    p->iBufBytesNum = fread(p->buf+k,1,SVA_STREAM_BUF_SIZE-k,p->f);
    if(p->iBufBytesNum == 0)
    {
      if(k>0)
      {
        p->uPre3Bytes = ((p->uPre3Bytes<<8) | p->buf[i]) & 0x00ffffff;
        if(p->uPre3Bytes < 4 && demulate_enable) 
        {
          p->uClearBits = (p->uClearBits << 6) | (p->buf[i] >> 2);
          p->iClearBitsNum += 6;
          StatBitsPtr->emulate_bits += 2;	//ITM_r2
        }
        else
        {
          p->uClearBits = (p->uClearBits << 8) | p->buf[i];
          p->iClearBitsNum += 8;
        }
        p->iBytePosition++;
        return 0;
      }
      else
      {
        return -1;//arrive at stream end
      }
    }
    else
    {
      for(j=0;j<k;j++) p->buf[j] = temp[j];
      p->iBufBytesNum += k;
      i = p->iBytePosition = 0;
    }
  }
  if(p->buf[i]==0 && p->buf[i+1]==0 && p->buf[i+2]==1)	return -2;// meet another start code
  p->uPre3Bytes = ((p->uPre3Bytes<<8) | p->buf[i]) & 0x00ffffff;
  if(p->uPre3Bytes < 4 && demulate_enable)
  {
    p->uClearBits = (p->uClearBits << 6) | (p->buf[i] >> 2);
    p->iClearBitsNum += 6;
    StatBitsPtr->emulate_bits += 2;	//ITM_r2
  }
  else
  {
    p->uClearBits = (p->uClearBits << 8) | p->buf[i];
    p->iClearBitsNum += 8;
  }
  p->iBytePosition++;
  return 0;
}


/*
*************************************************************************
* Function:
* Input:
* Output:
* Return:  0 : OK
-1 : arrive at stream end
-2 : meet another start code
* Attention:
*************************************************************************
*/
int read_n_bit(InputStream *p,int n,int *v)
{
  int r;
  unsigned int t;
  while(n > p->iClearBitsNum)
  {
    r = ClearNextByte( p );
    if(r)
    {
      if(r==-1)
      {
        if(p->iBufBytesNum - p->iBytePosition > 0)
          break;
      }
      return r;
    }
  }
  t = p->uClearBits;
  r = 32 - p->iClearBitsNum;
  *v = (t << r) >> (32 - n);
  p->iClearBitsNum -= n;
  return 0;
}
//==================================================================================

void OpenBitstreamFile (char *fn)
{
  OpenIRABS(pIRABS, fn);
}

void CloseBitstreamFile()
{
  CloseIRABS(pIRABS);
}


/*
*************************************************************************
* Function:For supporting multiple sequences in a stream
* Input:
* Output:
* Return: 
* Attention: 
* Author: 
*************************************************************************
*/
int IsEndOfBitstream ()
{
  int ret, seof, m;
  ret=feof(pIRABS->f);
  m = pIRABS->iBufBytesNum - pIRABS->iBytePosition;
  seof = ((ret) && (!pIRABS->iBufBytesNum)) || ((ret) && (m==1));
  return ((!seof)?(0):(1));
}

static int FindStartCode (unsigned char *Buf, int i)
{
  if(Buf[i]==0 && Buf[i+1]==0 && Buf[i+2]==1)
    return 1;
  else
    return 0;
}


/*
*************************************************************************
* Function:check slice start code
* Input:
* Output:
* Return: 
* Attention: 
*************************************************************************
*/
int checkstartcode()  
{
  int temp_i, temp_val;

  //multiple slice
  DecodingEnvironmentPtr dep_dp;

    dep_dp = &img->currentSlice->partArr[0].de_AEC;
    currStream->frame_bitoffset = arideco_bits_read(dep_dp);
  //multiple slice

  if (currStream->bitstream_length*8 - currStream->frame_bitoffset == 0)
    return 1;

  if(img->current_mb_nr == 0) 
  {
#if M38817_DATA_STUFFING
	  if (  currStream->bitstream_length*8 - currStream->frame_bitoffset <=9 
		  && currStream->bitstream_length*8 - currStream->frame_bitoffset > 0  ){
			  temp_i = currStream->bitstream_length*8 - currStream->frame_bitoffset; 
			  assert( temp_i >= 2 );
			  temp_val = get_uv(temp_i, "filling data") ;			
	  }
#else
    if (  currStream->bitstream_length*8 - currStream->frame_bitoffset <=8 
      && currStream->bitstream_length*8 - currStream->frame_bitoffset > 0  ){
        temp_i = currStream->bitstream_length*8 - currStream->frame_bitoffset; 
        assert( temp_i > 0 );
        temp_val = get_uv(temp_i, "filling data") ;			
    }
#endif
  }

  if(img->current_mb_nr == 0) 
  {
    if(StartCodePosition>4 && StartCodePosition<0x000fffff)							
      return 1;
    else
    {
      currStream->frame_bitoffset = currentbitoffset;
      return 0;
    }
  }

  if(img->current_mb_nr != 0) 
  {		
#if M38817_DATA_STUFFING
	  if (currStream->bitstream_length*8 - currStream->frame_bitoffset <= 9 
		  && currStream->bitstream_length*8 - currStream->frame_bitoffset >0){
			  temp_i = currStream->bitstream_length*8 - currStream->frame_bitoffset; 
			  assert( temp_i > 0 );
			  temp_val = get_uv(temp_i, "stuffing pattern") ;  //modified at ITM
			  assert(temp_i >= 2);
			  if ( temp_val == (3 << (temp_i -2))  && img->cod_counter <= 0   )
				  return 1; // last MB in current slice 
	  }
#else
    if (currStream->bitstream_length*8 - currStream->frame_bitoffset <= 8 
      && currStream->bitstream_length*8 - currStream->frame_bitoffset >0){
        temp_i = currStream->bitstream_length*8 - currStream->frame_bitoffset; 
        assert( temp_i > 0 );
        temp_val = get_uv(temp_i, "stuffing pattern") ;  //modified at ITM
        if ( temp_val == (1 << (temp_i -1))  && img->cod_counter <= 0   )
          return 1; // last MB in current slice 
    }
#endif
    return 0;       // not last MB in current slice 
    //---end				
  }

  return 1;
}



//------------------------------------------------------------
// buf          : buffer
// startcodepos : 
// length       : 
int GetOneUnit (unsigned char *buf,int *startcodepos,int *length)
{
  int i,j,k;
  i = NextStartCode(pIRABS);
  if(i!=0)
  {
    if(i==-1) printf("\narrive at stream end and start code is not found!");
    if(i==-2) printf("\np->iBytePosition error!");
    exit(i);
  }
  buf[0] = 0;
  buf[1] = 0;
  buf[2] = 1;
  *startcodepos = 3;
  i = read_n_bit(pIRABS,8,&j);
  buf[3] = (char)j;
  CheckType(buf[3]); 
  if(buf[3]==SEQUENCE_END_CODE)
  {
    *length = 4;
    return -1;
  }
  k = 4;
  while(1)
  {
    i = read_n_bit(pIRABS,8,&j);
    if(i<0) break;
    buf[k++] = (char)j;
  }
  if(pIRABS->iClearBitsNum>0)
  {
    int shift;
    shift = 8 - pIRABS->iClearBitsNum;
    i = read_n_bit(pIRABS,pIRABS->iClearBitsNum,&j);

    if(j!=0)  
      buf[k++] = (char)(j<<shift);
    StatBitsPtr->last_unit_bits += shift;  
  }
  *length = k;
  return k;
}





